package rss_GUI;

import java.awt.Toolkit;
import java.awt.Component;
import java.awt.event.KeyEvent;
import java.text.SimpleDateFormat;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.swing.DefaultListCellRenderer;
import javax.swing.RowSorter.SortKey;
import javax.swing.JComponent;
import javax.swing.JList;
import javax.swing.KeyStroke;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import rss_CODE.RadioStationSystem;
import rss_POJO.*;

/**
 *
 * @author RS-team
 */
public class ArtistManagement extends javax.swing.JFrame {

    private static final String dateRenderer = "EEE d MMM y";  // πρότυπο ημερομηνίας για εμφάνιση ή έλεγχο στις φόρμες

    private final RadioStationSystem rss;
    private final EntityManager em;
    private final MainMenu mm;

    private final String frameTitle;

    private Boolean addORedit;  // false: κλήση για EDIT, true: κλήση για ADD
    private Boolean editStatus;  // βρισκόμαστε σε κατάσταση editing των πεδίων της φόρμας ή όχι
    private Integer fieldFocused;  // ποιο πεδίο της φόρμας είναι focused
    private String tempTxt;  // προσωρινή μεταβλητή String πεδίων
    private final Artist tempA;  // προσωρινά αποθηκευμένος Artist
    private Artist a;

    
    /**
     * Creates new form ArtistManagement
     */
    public ArtistManagement(MainMenu mm) {
        this.rss = mm.getRSS();
        this.em = mm.getEM();
        this.mm = mm;
        this.addORedit = null;
        this.editStatus = false;
        this.fieldFocused = 0;
        this.a = new Artist();
        this.tempA = new Artist();
        this.tempTxt = "";
        initComponents();
        this.frameTitle = this.getTitle();
        jLabelARTISTmale.setVisible(false); jLabelARTISTfemale.setVisible(false);  // ούτε άρρεν ούτε θήλυ
        // κατάργηση ανεπιθύμητης τύπου enter λειτουργίας του space key για τα combo boxes cboxMusicGenre και cboxSex
        cboxMusicGenre.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE,0,false), "none");
        cboxSex.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE,0,false), "none");
        // αρχικοποίηση και καθαρισμός πεδίων φόρμας επεξεργασίας, reset combo boxes to top (το index -1 είναι το null)
        EnableDisableFields(false); cboxMusicGenre.setSelectedIndex(0); cboxSex.setSelectedIndex(0); ClearAllFields();
    }

    /* dynamic jpql query που αναζητά εγγραφή στο table Artist βάσει δοθέντος πεδίου και τιμής πεδίου */
    private Boolean searchArtist(String field, Object value) {
        try {
            if(addORedit) {
                // σε ADD mode, το αντικείμενο Artist a είναι καινούριο οπότε ελεγχω όλη τη ΒΔ
                em.createQuery("SELECT a FROM Artist a WHERE a." + field + "=:value", Artist.class)
                        .setParameter("value", value).getSingleResult();
            } else {
                // σε EDIT mode, πρέπει η αναζήτηση να εξαιρεί το ίδιο αντικείμενο Artist a μέσω του primary key
                em.createQuery("SELECT a FROM Artist a WHERE a." + field + "=:value AND a.artistid!=:id", Artist.class)
                        .setParameter("value", value).setParameter("id", a.getArtistid()).getSingleResult();
            }
            return true;  // η αναζήτηση ήταν επιτυχής αφού δεν συνέβει κάποιο σφάλμα
        } catch(NoResultException nrex) {
            // δεν βρέθηκε τίποτα, άρα η εγγραφή είναι μοναδική
            return false;
        } catch(Exception ex) {
            // γενικότερο σφάλμα σύνθεσης ή εκτέλεσης query αναζήτησης Artist
            rss.criticalErrorBox(this, "σφάλμα query αναζήτησης ARTIST " + field);
            // αγνοώντας οποιαδήποτε πιθανά νέα Exceptions προκύψουν,...
            try {
                em.close();  // οριστικό κλείσιμο EntityManager em
            } catch (Exception e) {
                // τίποτα πλέον, έτσι κι αλλιώς πάμε για κλείσιμο
            } finally {
                System.exit(4);  // έξοδος με errorlevel 4
                return null;  // άχρηστο αλλά αναγκαίο
            }
        } 
    }

    /* μέθοδος για πλήρη καθαρισμό των πεδίων και προετοιμασία του artist τύπου Artist για GarbageCollector */
    private void clearArtist(Artist artist) {
        // clear όλα τα πεδία
        artist.setFirstname(null);
        artist.setLastname(null);
        artist.setArtisticname(null);
        artist.setBirthplace(null);
        artist.setBirthdate(null);
        artist.setMusicgenreId(null);
        artist.setSex(null);
        artist.setMusicgenreId(null);
        // clear όλες τις λίστες του αντικειμένου
        if(artist.getAlbumCollection() != null) artist.getAlbumCollection().clear(); artist.setAlbumCollection(null);
        if(artist.getMusicGroupCollection() != null) artist.getMusicGroupCollection().clear(); artist.setMusicGroupCollection(null);
        artist.setArtistid(null);  // και τέλος το primary key
    }
    
    /* μέθοδος για καθαρισμό όλων των πεδίων της φόρμας επεξεργασίας */
    private void ClearAllFields() {
        txtFirstName.setText("");
        txtLastName.setText("");
        txtArtisticName.setText("");
        txtBirthPlace.setText("");
        txtBirthDate.setText("");
        cboxMusicGenre.setSelectedIndex(-1);  // index -1 είναι το null
        cboxSex.setSelectedIndex(-1);  // index -1 είναι το null
        jLabelARTISTmale.setVisible(false); jLabelARTISTfemale.setVisible(false);  // ούτε άρρεν ούτε θήλυ
    }

    /* μέθοδος για αντιγραφή πεδίων σε Artist toA από Artist fromA */
    private void CopyAllFields(Artist toA, Artist fromA) {
        toA.setFirstname(fromA.getFirstname());
        toA.setLastname(fromA.getLastname());
        toA.setArtisticname(fromA.getArtisticname());
        toA.setBirthplace(fromA.getBirthplace());
        toA.setBirthdate(fromA.getBirthdate());
        toA.setMusicgenreId(fromA.getMusicgenreId());
        toA.setSex(fromA.getSex());
        if(fromA.getAlbumCollection() != null) {  // αν η πηγαία λίστα Άλμπουμ του fromA δεν είναι null
            if(toA.getAlbumCollection() == null)  // αν η λίστα προορισμού Άλμπουμ του toA είναι null, δημιούργησε νέα empty list
                toA.setAlbumCollection(org.jdesktop.observablecollections.ObservableCollections.observableList(new ArrayList<Album>()));
            toA.getAlbumCollection().clear(); toA.getAlbumCollection().addAll(fromA.getAlbumCollection());  // καθάρισε και κάνε την αντιγραφή
        } else toA.setAlbumCollection(null);  // αλλιώς θέσε null και την λίστα προορισμού του toA
        if(fromA.getMusicGroupCollection() != null) {  // αν η πηγαία λίστα Συγκροτημάτων του fromA δεν είναι null
            if(toA.getMusicGroupCollection() == null)  // αν η λίστα προορισμού Συγκροτημάτων του toA είναι null, δημιούργησε νέα empty list
                toA.setMusicGroupCollection(org.jdesktop.observablecollections.ObservableCollections.observableList(new ArrayList<MusicGroup>()));
            toA.getAlbumCollection().clear(); toA.getMusicGroupCollection().addAll(fromA.getMusicGroupCollection());  // καθάρισε και κάνε την αντιγραφή
        } else toA.setMusicGroupCollection(null);  // αλλιώς θέσε null και την λίστα προορισμού του toA
        toA.setArtistid(fromA.getArtistid());  // και τέλος το primary key
    }
    
    /* μέθοδος για ενημέρωση πεδίων φόρμας με τις τιμές του επιλεγμένου αντικειμενου a τύπου Artist */
    private void UpdateAllFields() {
        txtFirstName.setText(a.getFirstname() == null? "" : a.getFirstname());
        txtLastName.setText(a.getLastname() == null? "" : a.getLastname());
        txtArtisticName.setText(a.getArtisticname() == null? "" : a.getArtisticname());
        txtBirthPlace.setText(a.getBirthplace() == null? "" : a.getBirthplace());
        txtBirthDate.setText(a.getBirthdate() == null? "" : new SimpleDateFormat(dateRenderer).format(a.getBirthdate()));  // επιπλέον μορφοποίησε ημερομηνία
        cboxMusicGenre.setSelectedIndex(a.getMusicgenreId() == null? -1 : listMusicGenre.indexOf(a.getMusicgenreId()));
        cboxSex.setSelectedIndex(a.getSex() == null? -1 : (a.getSex()+1));  // +1 για απορρόφηση του ? στη θέση 0 ως null
        if(cboxSex.getSelectedIndex() == 1) {  // θήλυ
            jLabelARTISTmale.setVisible(false); jLabelARTISTfemale.setVisible(true);
        } else if(cboxSex.getSelectedIndex() == 2) {  // άρρεν
            jLabelARTISTmale.setVisible(true); jLabelARTISTfemale.setVisible(false);
        } else { jLabelARTISTmale.setVisible(false); jLabelARTISTfemale.setVisible(false); }  // ούτε άρρεν ούτε θήλυ
    }
    
    /* σύγκριση πεδίων φόρμας επεξεργασίας με το επιλεγμένο αντικειμενο a τύπου Artist */
    private Boolean compareAllFields() {
        // σύγκρινε ένα ένα τα πεδία για διαφορές με τα αντίστοιχα του επιλεγμένου Artist a προσέχοντας πάντα τα nulls
        if(a.getArtisticname() == null? !txtArtisticName.getText().isEmpty() : !txtArtisticName.getText().equals(a.getArtisticname())) return false;
        if(a.getFirstname() == null? !txtFirstName.getText().isEmpty() : !txtFirstName.getText().equals(a.getFirstname())) return false;
        if(a.getLastname() == null? !txtLastName.getText().isEmpty() : !txtLastName.getText().equals(a.getLastname())) return false;
        if(a.getBirthplace() == null? !txtBirthPlace.getText().isEmpty() : !txtBirthPlace.getText().equals(a.getBirthplace())) return false;
        if(rss.validateDate(txtBirthDate.getText(), dateRenderer) == null) return false;  // για την ημερομηνία επιπλέον έλεγχοι μορφοποίησης και εγκυρότητας
        else if(a.getBirthdate() == null? !rss.validateDate(txtBirthDate.getText(), dateRenderer).isEmpty() : !rss.validateDate(txtBirthDate.getText(), dateRenderer).equals(new SimpleDateFormat(dateRenderer).format(a.getBirthdate()))) return false;
        if(a.getMusicgenreId() == null? cboxMusicGenre.getSelectedIndex() > 0 : (cboxMusicGenre.getSelectedIndex() > 0? !cboxMusicGenre.getSelectedItem().equals(a.getMusicgenreId()) : true)) return false;
        if(a.getSex() == null? cboxSex.getSelectedIndex() > 0 : cboxSex.getSelectedIndex() != (a.getSex()+1)) return false;  // +1 για απορρόφηση του ? στη θέση 0 ως null
        return true;  // όλα τα πεδία ταυτίζονται
    }

    /* πραγματοποιεί ελέγχους σε όλα τα πεδία με πρόβλεψη αλλαγής nullable ή uniqueness κατάστασης από τα POJOs */
    private Boolean validateAllFields() {
        // έλεγχοι για το FirstName
        if(!txtFirstName.getText().equals(txtFirstName.getText().replace("\\",""))) {  // έλεγχος για ανεπιθύμητους \ χαρακτήρες
            if(!mm.getExpertMode()) { txtFirstName.grabFocus(); rss.infoBox(this, "Δεν επιτρέπονται  \\  σε αλφαριθμητικά"); return false; }  // αν όχι σε ExpertMode
            else { a.setFirstname(txtFirstName.getText().replace("\\","").trim()); }  // αλλιώς σιωπηλά αφαίρεσε ανεπιθύμητους \ χαρακτήρες και κενά κατέρωθεν του αλφαριθμητικού
        } else a.setFirstname(txtFirstName.getText().trim());  // αφαίρεσε μόνο τα κενά εκατέρωθεν του αλφαριθμητικού
        if(a.getFirstname().isEmpty()) a.setFirstname(null);  // αν είναι κενό μηδένισέ το σε null
        if(a.getFirstname() != null && a.getFirstname().length() > rss.fieldLength(this, Artist.class, "firstname")) {  // περιορισμός αλφαριθμητικού στο μήκος που δηλώνεται στο POJO
            a.setFirstname(a.getFirstname().substring(0, rss.fieldLength(this, Artist.class, "firstname"))); }
        if(a.getFirstname() != null && rss.fieldUnique(this, Artist.class, "firstname") && searchArtist("firstname", a.getFirstname())) {  // δυνητικός έλεγχος μοναδικότητας με query
            txtFirstName.grabFocus(); rss.warningBox(this, "το ΟΝΟΜΑ που εισάγατε υπάρχει ήδη  "); return false; }
        if(a.getFirstname() == null && !rss.fieldNullable(this, Artist.class, "firstname")) {  // δυνητικός έλεγχος nullability
            txtFirstName.grabFocus(); rss.warningBox(this, "το ΟΝΟΜΑ δεν μπορεί να είναι κενό  "); return false; }
        // έλεγχοι για το LastName
        if(!txtLastName.getText().equals(txtLastName.getText().replace("\\",""))) {  // έλεγχος για ανεπιθύμητους \ χαρακτήρες
            if(!mm.getExpertMode()) { txtLastName.grabFocus(); rss.infoBox(this, "Δεν επιτρέπονται  \\  σε αλφαριθμητικά"); return false; }  // αν όχι σε ExpertMode
            else { a.setLastname(txtLastName.getText().replace("\\","").trim()); }  // αλλιώς σιωπηλά αφαίρεσε ανεπιθύμητους \ χαρακτήρες και κενά κατέρωθεν του αλφαριθμητικού
        } else a.setLastname(txtLastName.getText().trim());  // αφαίρεσε μόνο τα κενά εκατέρωθεν του αλφαριθμητικού
        if(a.getLastname().isEmpty()) a.setLastname(null);  // αν είναι κενό μηδένισέ το
        if(a.getLastname() != null && a.getLastname().length() > rss.fieldLength(this, Artist.class, "lastname")) {  // περιορισμός αλφαριθμητικού στο μήκος που δηλώνεται στο POJO
            a.setLastname(a.getLastname().substring(0, rss.fieldLength(this, Artist.class, "lastname"))); }
        if(a.getLastname() != null && rss.fieldUnique(this, Artist.class, "lastname") && searchArtist("lastname", a.getLastname())) {  // δυνητικός έλεγχος μοναδικότητας με query
            txtLastName.grabFocus(); rss.warningBox(this, "το ΕΠΙΘΕΤΟ που εισάγατε υπάρχει ήδη  "); return false; }
        if(a.getLastname() == null && !rss.fieldNullable(this,Artist.class, "lastname")) {  // δυνητικός έλεγχος nullability
            txtLastName.grabFocus(); rss.warningBox(this, "το ΕΠΙΘΕΤΟ δεν μπορεί να είναι κενό  "); return false; }
        // έλεγχοι για το ArtisticName
        if(!txtArtisticName.getText().equals(txtArtisticName.getText().replace("\\",""))) {  // έλεγχος για ανεπιθύμητους \ χαρακτήρες
            if(!mm.getExpertMode()) { txtArtisticName.grabFocus(); rss.infoBox(this, "Δεν επιτρέπονται  \\  σε αλφαριθμητικά"); return false; }  // αν όχι σε ExpertMode
            else { a.setArtisticname(txtArtisticName.getText().replace("\\","").trim()); }  // αλλιώς σιωπηλά αφαίρεσε ανεπιθύμητους \ χαρακτήρες και κενά κατέρωθεν του αλφαριθμητικού
        } else a.setArtisticname(txtArtisticName.getText().trim());  // αφαίρεσε μόνο τα κενά εκατέρωθεν του αλφαριθμητικού
        if(a.getArtisticname().isEmpty()) {  // αν είναι κενό, ειδική προσθήκη για το ArtisticName ως παράθεση FirstName και Lastname
            a.setArtisticname((a.getFirstname() == null? "" : a.getFirstname()) + " " + (a.getLastname() == null? "" : a.getLastname()));
            if(a.getArtisticname() != null) a.setArtisticname(a.getArtisticname().trim());  // αφαίρεσε πάλι πιθανά κενά εκατέρωθεν του αλφαριθμητικού
            if(a.getArtisticname().isEmpty()) a.setArtisticname(null); else txtArtisticName.setText(a.getArtisticname()); }  // και αν παραμένει ακόμα κενό μηδένισέ το, αλλιώς επικαιροποίησε το πεδίο
        if(a.getArtisticname() != null && a.getArtisticname().length() > rss.fieldLength(this, Artist.class, "artisticname")) {  // περιορισμός αλφαριθμητικού στο μήκος που δηλώνεται στο POJO
            a.setArtisticname(a.getArtisticname().substring(0, rss.fieldLength(this, Artist.class, "artisticname"))); }
        if(a.getArtisticname() != null && rss.fieldUnique(this, Artist.class, "artisticname") && searchArtist("artisticname", a.getArtisticname())) {  // δυνητικός έλεγχος μοναδικότητας με query
            txtArtisticName.grabFocus(); rss.warningBox(this, "το ΚΑΛΛΙΤΕΧΝΙΚΟ ΨΕΥΔΩΝΥΜΟ πρέπει να είναι μοναδικό    "); return false; }
        if(a.getArtisticname() == null && !rss.fieldNullable(this, Artist.class, "artisticname")) {  // δυνητικός έλεγχος nullability
            txtArtisticName.grabFocus(); rss.warningBox(this, "το ΚΑΛΛΙΤΕΧΝΙΚΟ ΨΕΥΔΩΝΥΜΟ δεν μπορεί να είναι κενό    "); return false; }
        // έλεγχοι για το BirthPlace
        if(!txtBirthPlace.getText().equals(txtBirthPlace.getText().replace("\\",""))) {  // έλεγχος για ανεπιθύμητους \ χαρακτήρες
            if(!mm.getExpertMode()) { txtBirthPlace.grabFocus(); rss.infoBox(this, "Δεν επιτρέπονται  \\  σε αλφαριθμητικά"); return false; }  // αν όχι σε ExpertMode
            else { a.setBirthplace(txtBirthPlace.getText().replace("\\","").trim()); }  // αλλιώς σιωπηλά αφαίρεσε ανεπιθύμητους \ χαρακτήρες και κενά κατέρωθεν του αλφαριθμητικού
        } else a.setBirthplace(txtBirthPlace.getText().trim());  // αφαίρεσε μόνο τα κενά εκατέρωθεν του αλφαριθμητικού
        if(a.getBirthplace().isEmpty()) a.setBirthplace(null);  // αν είναι κενό μηδένισέ το
        if(a.getBirthplace() != null && a.getBirthplace().length() > rss.fieldLength(this, Artist.class, "birthplace")) {  // περιορισμός αλφαριθμητικού στο μήκος που δηλώνεται στο POJO
            a.setBirthplace(a.getBirthplace().substring(0, rss.fieldLength(this, Artist.class, "birthplace"))); }
        if(a.getBirthplace() != null && rss.fieldUnique(this, Artist.class, "birthplace") && searchArtist("birthplace", a.getBirthplace())) {  // δυνητικός έλεγχος μοναδικότητας με query
            txtBirthPlace.grabFocus(); rss.warningBox(this, "ο ΤΟΠΟΣ ΚΑΤΑΓΩΓΗΣ πρέπει να είναι μοναδικός  "); return false; }
        if(a.getBirthplace() == null && !rss.fieldNullable(this, Artist.class, "birthplace")) {  // δυνητικός έλεγχος nullability
            txtBirthPlace.grabFocus(); rss.warningBox(this, "ο ΤΟΠΟΣ ΚΑΤΑΓΩΓΗΣ δεν μπορεί να είναι κενός  "); return false; }
        // έλεγχοι για το BirthDate
        if(!mm.getExpertMode() && rss.validateDate(txtBirthDate.getText(), dateRenderer) == null) { txtBirthDate.grabFocus(); rss.infoBox(this, "Η ημερομηνία δεν είναι έγκυρη"); return false; }  // όχι ExpertMode και μη έγκυρη ημερομηνία
        else { a.setBirthdate(rss.validDate(rss.validateDate(txtBirthDate.getText(), dateRenderer), dateRenderer)); }  // αλλιώς αποθήκευση έγκυρα μορφοποιημένης κατά το δοθέν πρότυπο ή σιωπηρά null αν είμαστε σε ExpertMode και δεν είναι έγκυρη
        if(a.getBirthdate() != null && rss.fieldUnique(this, Artist.class, "birthdate") && searchArtist("birthdate", a.getBirthdate())) {  // δυνητικός έλεγχος μοναδικότητας με query
            txtBirthDate.grabFocus(); rss.warningBox(this, "η ΗΜΕΡΟΜΗΝΙΑ πρέπει να είναι μοναδική  "); return false; }
        if(a.getBirthdate() == null && !rss.fieldNullable(this, Artist.class, "birthdate")) {  // δυνητικός έλεγχος nullability
            txtBirthDate.grabFocus(); rss.warningBox(this, "η ΗΜΕΡΟΜΗΝΙΑ δεν μπορεί να είναι κενή  "); return false; }
        // έλεγχοι για το MusicGenre
        a.setMusicgenreId((MusicGenre) (cboxMusicGenre.getSelectedIndex() > 0? cboxMusicGenre.getSelectedItem() : null));  // πάρε το κανονικό πεδίο από τη λίστα με απορρόφηση του ? ως null
        if(a.getMusicgenreId() != null && rss.fieldUnique(this, Artist.class, "musicgenreId") && searchArtist("musicgenreId", a.getMusicgenreId())) {  // δυνητικός έλεγχος μοναδικότητας με query
            cboxMusicGenre.grabFocus(); rss.warningBox(this, "το ΕΙΔΟΣ ΜΟΥΣΙΚΗΣ πρέπει να είναι μοναδικό  "); return false; }
        if(a.getMusicgenreId() == null && !rss.fieldNullable(this, Artist.class, "musicgenreId")) {  // δυνητικός έλεγχος nullability
            cboxMusicGenre.grabFocus(); rss.warningBox(this, "το ΕΙΔΟΣ ΜΟΥΣΙΚΗΣ δεν μπορεί να είναι κενό  "); return false; }
        // έλεγχοι για το Sex
        a.setSex((Integer) (cboxSex.getSelectedIndex() > 0? (cboxSex.getSelectedIndex()-1) : null));  // πάρε το κανονικό πεδίο από τη λίστα και -1 για απορρόφηση του ? στη θέση 0 ως null
        if(a.getSex() != null && rss.fieldUnique(this, Artist.class, "sex") && searchArtist("sex", a.getSex())) {  // δυνητικός έλεγχος μοναδικότητας με query
            cboxSex.grabFocus(); rss.warningBox(this, "το ΦΥΛΟ πρέπει να είναι μοναδικό "); return false; }
        if(a.getSex() == null && !rss.fieldNullable(this, Artist.class, "sex")) {  // δυνητικός έλεγχος nullability
            cboxSex.grabFocus(); rss.warningBox(this, "το ΦΥΛΟ δεν μπορεί να είναι κενό "); return false; }
        return true;  // επιτέλους, όλα ΟΚ!
    }
    
    /* ενεργοποίηση/απενεργοποίηση επιθυμητών components */
    // buttons και components εκτός από jTable και jPanel εκ κατασκευής non-focusable, επιλεκτικό components enabling και focusing
    private void EnableDisableFields(Boolean flag) {
        // για τον πίνακα Καλλιτεχνών και σχετικών κουμπιών
        //jScrollPaneTABLE.getVerticalScrollBar().setEnabled(!flag);
        jScrollPaneMAINTABLE.setEnabled(!flag); jLabelMAINTABLE.setEnabled(!flag);
        jButtonADD.setEnabled(!flag); jButtonEDIT.setEnabled(!flag); jButtonREMOVE.setEnabled(!flag);
        ArtistTABLE.setEnabled(!flag); ArtistTABLE.getTableHeader().setEnabled(!flag);
        ArtistTABLE.setFocusable(!flag);  // reset/set focus στο jTable
        // για τα πεδία επεξεργασίας Καλλιτέχνη και σχετικών κουμπιών
        jButtonSAVE.setEnabled(flag); jButtonCANCEL.setEnabled(flag);
        for (Component c : jPanelFORM.getComponents()) c.setEnabled(flag);
        jLabelFORM.setEnabled(flag); jPanelFORM.setEnabled(flag);  // τελευταίο το jPanel
        jPanelFORM.setFocusable(flag);  // set/reset focus στο jPanel
        jLabelARTISTmale.setEnabled(flag); jLabelARTISTfemale.setEnabled(flag);  // άρρεν ή θήλυ
        fieldFocused = 0; editStatus = flag;  // set/reset κατάσταση επεξεργασίας
        
    }
    
    /* overrides τον default τρόπο rendering σε comboBox με δυνατότητα επιλογής πεδίου από object λίστας */
    // usage: drag&drop class ή στο post-preation code του πίνακα προσθέτω [jComboBox name].setRenderer(new MusicGenreNameRenderer());
    private class MusicGenreNameRenderer extends DefaultListCellRenderer {
        @Override
        public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
            super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
            if((value != null) && (value instanceof MusicGenre)) {
                MusicGenre musicgenre = (MusicGenre) value;
                setText(musicgenre.getName());
            }
            return this;
        }
    }
    

    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {
        bindingGroup = new org.jdesktop.beansbinding.BindingGroup();

        try {
            queryArtist = java.beans.Beans.isDesignTime() ? null : em.createQuery("SELECT a FROM Artist a");
            listArtist = java.beans.Beans.isDesignTime() ? java.util.Collections.emptyList() : org.jdesktop.observablecollections.ObservableCollections.observableList(queryArtist.getResultList());
        } catch(Exception ex) {
            rss.warningBox(this, "σφάλμα κατά την ανάγνωση ARTISTs");
            // αν υπάρχει προηγούμενη την μηδενίζω και την στέλνω στον GC, κατόπιν δημιουργώ και επιστρέφω νέα κενή λίστα λόγω γενικότερου σφάλματος
            if(listArtist != null) listArtist.clear(); listArtist = null; listArtist = org.jdesktop.observablecollections.ObservableCollections.observableList(new ArrayList<Artist>());
        }
        try {
            queryMusicGenre = java.beans.Beans.isDesignTime() ? null : em.createQuery("SELECT mg FROM MusicGenre mg");
            listMusicGenre = java.beans.Beans.isDesignTime() ? java.util.Collections.emptyList() : org.jdesktop.observablecollections.ObservableCollections.observableList(queryMusicGenre.getResultList());
        } catch(Exception ex) {
            rss.warningBox(this, "σφάλμα κατά την ανάγνωση MUSICGENREs");
            // αν υπάρχει προηγούμενη την μηδενίζω και την στέλνω στον GC, κατόπιν δημιουργώ και επιστρέφω νέα κενή λίστα λόγω γενικότερου σφάλματος
            if(listMusicGenre != null) listMusicGenre.clear(); listMusicGenre = null; listMusicGenre = org.jdesktop.observablecollections.ObservableCollections.observableList(new ArrayList<MusicGenre>());
        }
        // ταξινόμηση λίστας Κατηγοριών Μουσικής ως προς Name
        Collections.sort(listMusicGenre, new Comparator<MusicGenre>() {
            @Override
            public int compare(MusicGenre musicgenre1, MusicGenre musicgenre2) {
                return  musicgenre1.getName().compareTo(musicgenre2.getName());  // αύξουσα διάταξη
            }
        });
        listMusicGenre.add(0, new MusicGenre("?"));  // προσθέτω το ? για πιθανή null επιλογή
        jLabelRSTEAM = new javax.swing.JLabel();
        jLabelRSSLOGO = new javax.swing.JLabel();
        jSeparatorLINE = new javax.swing.JSeparator();
        jLabelMAINTABLE = new javax.swing.JLabel();
        jScrollPaneMAINTABLE = new javax.swing.JScrollPane();
        ArtistTABLE = new javax.swing.JTable();
        ArtistTABLE.setSelectionModel(new MainMenu.ForcedListSelectionModel());

        ArtistTABLE.getTableHeader().addMouseListener(new MainMenu.TableHeaderMouseListener(ArtistTABLE));
        jLabelFORM = new javax.swing.JLabel();
        jPanelFORM = new javax.swing.JPanel();
        txtArtisticName = new javax.swing.JTextField();
        txtFirstName = new javax.swing.JTextField();
        txtLastName = new javax.swing.JTextField();
        txtBirthPlace = new javax.swing.JTextField();
        txtBirthDate = new javax.swing.JTextField();
        cboxMusicGenre = new javax.swing.JComboBox();
        cboxMusicGenre.setRenderer(new MusicGenreNameRenderer());
        cboxSex = new javax.swing.JComboBox();
        jLabelARTISTmale = new javax.swing.JLabel();
        jLabelARTISTfemale = new javax.swing.JLabel();
        jButtonRETURN = new javax.swing.JButton();
        jButtonADD = new javax.swing.JButton();
        jButtonEDIT = new javax.swing.JButton();
        jButtonREMOVE = new javax.swing.JButton();
        jButtonSAVE = new javax.swing.JButton();
        jButtonCANCEL = new javax.swing.JButton();
        jLabelBACKGROUND = new javax.swing.JLabel();

        setDefaultCloseOperation(javax.swing.WindowConstants.DO_NOTHING_ON_CLOSE);
        setTitle("RADIO STATION SYSTEM - Διαχείριση Καλλιτεχνών");
        setLocationByPlatform(true);
        setMinimumSize(new java.awt.Dimension(806, 604));
        setName("ARTISTsMANAGEMENT"); // NOI18N
        setResizable(false);
        addWindowFocusListener(new java.awt.event.WindowFocusListener() {
            public void windowGainedFocus(java.awt.event.WindowEvent evt) {
                formWindowGainedFocus(evt);
            }
            public void windowLostFocus(java.awt.event.WindowEvent evt) {
            }
        });
        addWindowListener(new java.awt.event.WindowAdapter() {
            public void windowClosed(java.awt.event.WindowEvent evt) {
                formWindowClosed(evt);
            }
            public void windowClosing(java.awt.event.WindowEvent evt) {
                formWindowClosing(evt);
            }
            public void windowOpened(java.awt.event.WindowEvent evt) {
                formWindowOpened(evt);
            }
        });
        getContentPane().setLayout(null);

        jLabelRSTEAM.setIcon(new javax.swing.ImageIcon(getClass().getResource("/misc/design/RSTeam_maxi.png"))); // NOI18N
        jLabelRSTEAM.setFocusable(false);
        getContentPane().add(jLabelRSTEAM);
        jLabelRSTEAM.setBounds(516, 533, 284, 43);

        jLabelRSSLOGO.setIcon(new javax.swing.ImageIcon(getClass().getResource("/misc/design/RSS-Logo_maxi.png"))); // NOI18N
        jLabelRSSLOGO.setFocusable(false);
        getContentPane().add(jLabelRSSLOGO);
        jLabelRSSLOGO.setBounds(12, 12, 180, 152);

        jSeparatorLINE.setAutoscrolls(true);
        getContentPane().add(jSeparatorLINE);
        jSeparatorLINE.setBounds(10, 534, 780, 2);

        jLabelMAINTABLE.setFont(new java.awt.Font("Arial", 1, 14)); // NOI18N
        jLabelMAINTABLE.setLabelFor(jScrollPaneMAINTABLE);
        jLabelMAINTABLE.setText("   ΠΙΝΑΚΑΣ ΚΑΛΛΙΤΕΧΝΩΝ");
        jLabelMAINTABLE.setBorder(new javax.swing.border.SoftBevelBorder(javax.swing.border.BevelBorder.RAISED));
        jLabelMAINTABLE.setFocusable(false);
        jLabelMAINTABLE.setMaximumSize(new java.awt.Dimension(180, 20));
        jLabelMAINTABLE.setMinimumSize(new java.awt.Dimension(180, 20));
        jLabelMAINTABLE.setOpaque(true);
        jLabelMAINTABLE.setPreferredSize(new java.awt.Dimension(180, 20));
        jLabelMAINTABLE.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                jLabelMAINTABLEMouseClicked(evt);
            }
        });
        getContentPane().add(jLabelMAINTABLE);
        jLabelMAINTABLE.setBounds(240, 30, 190, 20);

        jScrollPaneMAINTABLE.setFocusable(false);
        jScrollPaneMAINTABLE.setFont(new java.awt.Font("Tahoma", 0, 12)); // NOI18N
        jScrollPaneMAINTABLE.setMinimumSize(new java.awt.Dimension(31, 31));
        jScrollPaneMAINTABLE.setPreferredSize(new java.awt.Dimension(530, 183));

        ArtistTABLE.setAutoCreateRowSorter(true);
        ArtistTABLE.setFont(new java.awt.Font("Tahoma", 0, 12)); // NOI18N
        ArtistTABLE.setMaximumSize(new java.awt.Dimension(2147483647, 0));
        ArtistTABLE.setMinimumSize(new java.awt.Dimension(424, 0));

        org.jdesktop.swingbinding.JTableBinding jTableBinding = org.jdesktop.swingbinding.SwingBindings.createJTableBinding(org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ_WRITE, listArtist, ArtistTABLE);
        org.jdesktop.swingbinding.JTableBinding.ColumnBinding columnBinding = jTableBinding.addColumnBinding(org.jdesktop.beansbinding.ELProperty.create("${firstname}"));
        columnBinding.setColumnName("ΟΝΟΜΑ");
        columnBinding.setColumnClass(String.class);
        columnBinding.setEditable(false);
        columnBinding = jTableBinding.addColumnBinding(org.jdesktop.beansbinding.ELProperty.create("${lastname}"));
        columnBinding.setColumnName("ΕΠΙΘΕΤΟ");
        columnBinding.setColumnClass(String.class);
        columnBinding.setEditable(false);
        columnBinding = jTableBinding.addColumnBinding(org.jdesktop.beansbinding.ELProperty.create("${artisticname}"));
        columnBinding.setColumnName("ΚΑΛΛΙΤΕΧΝΙΚΟ ΨΕΥΔΩΝΥΜΟ");
        columnBinding.setColumnClass(String.class);
        columnBinding.setEditable(false);
        columnBinding = jTableBinding.addColumnBinding(org.jdesktop.beansbinding.ELProperty.create("${musicgenreId.name}"));
        columnBinding.setColumnName("ΕΙΔΟΣ");
        columnBinding.setColumnClass(String.class);
        columnBinding.setEditable(false);
        columnBinding = jTableBinding.addColumnBinding(org.jdesktop.beansbinding.ELProperty.create("${sex}"));
        columnBinding.setColumnName("Φ");
        columnBinding.setColumnClass(Integer.class);
        columnBinding.setEditable(false);
        bindingGroup.addBinding(jTableBinding);
        jTableBinding.bind();
        ArtistTABLE.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                ArtistTABLEMouseClicked(evt);
            }
            public void mousePressed(java.awt.event.MouseEvent evt) {
                ArtistTABLEMousePressed(evt);
            }
            public void mouseReleased(java.awt.event.MouseEvent evt) {
                ArtistTABLEMouseReleased(evt);
            }
        });
        ArtistTABLE.addKeyListener(new java.awt.event.KeyAdapter() {
            public void keyPressed(java.awt.event.KeyEvent evt) {
                ArtistTABLEKeyPressed(evt);
            }
            public void keyReleased(java.awt.event.KeyEvent evt) {
                ArtistTABLEKeyReleased(evt);
            }
        });
        jScrollPaneMAINTABLE.setViewportView(ArtistTABLE);
        if (ArtistTABLE.getColumnModel().getColumnCount() > 0) {
            ArtistTABLE.getColumnModel().getColumn(0).setMinWidth(68);
            ArtistTABLE.getColumnModel().getColumn(1).setMinWidth(78);
            ArtistTABLE.getColumnModel().getColumn(2).setMinWidth(186);
            ArtistTABLE.getColumnModel().getColumn(3).setMinWidth(60);
            ArtistTABLE.getColumnModel().getColumn(4).setMinWidth(32);
            ArtistTABLE.getColumnModel().getColumn(4).setPreferredWidth(32);
            ArtistTABLE.getColumnModel().getColumn(4).setMaxWidth(32);
            ArtistTABLE.getColumnModel().getColumn(4).setCellRenderer(new MainMenu.SexCellRenderer());
        }

        getContentPane().add(jScrollPaneMAINTABLE);
        jScrollPaneMAINTABLE.setBounds(230, 50, 530, 183);

        jLabelFORM.setFont(new java.awt.Font("Arial", 1, 14)); // NOI18N
        jLabelFORM.setLabelFor(jPanelFORM);
        jLabelFORM.setText("   ΣΤΟΙΧΕΙΑ ΚΑΛΛΙΤΕΧΝΗ");
        jLabelFORM.setBorder(new javax.swing.border.SoftBevelBorder(javax.swing.border.BevelBorder.RAISED));
        jLabelFORM.setEnabled(false);
        jLabelFORM.setFocusable(false);
        jLabelFORM.setMaximumSize(new java.awt.Dimension(180, 20));
        jLabelFORM.setMinimumSize(new java.awt.Dimension(180, 180));
        jLabelFORM.setOpaque(true);
        jLabelFORM.setPreferredSize(new java.awt.Dimension(180, 20));
        getContentPane().add(jLabelFORM);
        jLabelFORM.setBounds(50, 279, 180, 20);

        jPanelFORM.setBorder(new javax.swing.border.SoftBevelBorder(javax.swing.border.BevelBorder.RAISED));
        jPanelFORM.setEnabled(false);
        jPanelFORM.setPreferredSize(new java.awt.Dimension(720, 180));
        jPanelFORM.addFocusListener(new java.awt.event.FocusAdapter() {
            public void focusGained(java.awt.event.FocusEvent evt) {
                jPanelFORMFocusGained(evt);
            }
        });
        jPanelFORM.addKeyListener(new java.awt.event.KeyAdapter() {
            public void keyPressed(java.awt.event.KeyEvent evt) {
                jPanelFORMKeyPressed(evt);
            }
        });

        txtArtisticName.setFont(new java.awt.Font("Tahoma", 0, 12)); // NOI18N
        txtArtisticName.setBorder(javax.swing.BorderFactory.createTitledBorder("ΚΑΛΛΙΤΕΧΝΙΚΟ ΨΕΥΔΩΝΥΜΟ"));
        txtArtisticName.setEnabled(false);
        txtArtisticName.setMinimumSize(new java.awt.Dimension(100, 40));
        txtArtisticName.setPreferredSize(new java.awt.Dimension(100, 40));
        txtArtisticName.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                txtArtisticNameActionPerformed(evt);
            }
        });
        txtArtisticName.addFocusListener(new java.awt.event.FocusAdapter() {
            public void focusGained(java.awt.event.FocusEvent evt) {
                txtArtisticNameFocusGained(evt);
            }
            public void focusLost(java.awt.event.FocusEvent evt) {
                txtArtisticNameFocusLost(evt);
            }
        });
        txtArtisticName.addKeyListener(new java.awt.event.KeyAdapter() {
            public void keyPressed(java.awt.event.KeyEvent evt) {
                txtArtisticNameKeyPressed(evt);
            }
        });

        txtFirstName.setFont(new java.awt.Font("Tahoma", 0, 12)); // NOI18N
        txtFirstName.setBorder(javax.swing.BorderFactory.createTitledBorder("ΟΝΟΜΑ"));
        txtFirstName.setEnabled(false);
        txtFirstName.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                txtFirstNameActionPerformed(evt);
            }
        });
        txtFirstName.addFocusListener(new java.awt.event.FocusAdapter() {
            public void focusGained(java.awt.event.FocusEvent evt) {
                txtFirstNameFocusGained(evt);
            }
            public void focusLost(java.awt.event.FocusEvent evt) {
                txtFirstNameFocusLost(evt);
            }
        });
        txtFirstName.addKeyListener(new java.awt.event.KeyAdapter() {
            public void keyPressed(java.awt.event.KeyEvent evt) {
                txtFirstNameKeyPressed(evt);
            }
        });

        txtLastName.setFont(new java.awt.Font("Tahoma", 0, 12)); // NOI18N
        txtLastName.setBorder(javax.swing.BorderFactory.createTitledBorder("ΕΠΙΘΕΤΟ"));
        txtLastName.setEnabled(false);
        txtLastName.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                txtLastNameActionPerformed(evt);
            }
        });
        txtLastName.addFocusListener(new java.awt.event.FocusAdapter() {
            public void focusGained(java.awt.event.FocusEvent evt) {
                txtLastNameFocusGained(evt);
            }
            public void focusLost(java.awt.event.FocusEvent evt) {
                txtLastNameFocusLost(evt);
            }
        });
        txtLastName.addKeyListener(new java.awt.event.KeyAdapter() {
            public void keyPressed(java.awt.event.KeyEvent evt) {
                txtLastNameKeyPressed(evt);
            }
        });

        txtBirthPlace.setFont(new java.awt.Font("Tahoma", 0, 12)); // NOI18N
        txtBirthPlace.setBorder(javax.swing.BorderFactory.createTitledBorder("ΤΟΠΟΣ ΚΑΤΑΓΩΓΗΣ"));
        txtBirthPlace.setEnabled(false);
        txtBirthPlace.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                txtBirthPlaceActionPerformed(evt);
            }
        });
        txtBirthPlace.addFocusListener(new java.awt.event.FocusAdapter() {
            public void focusGained(java.awt.event.FocusEvent evt) {
                txtBirthPlaceFocusGained(evt);
            }
            public void focusLost(java.awt.event.FocusEvent evt) {
                txtBirthPlaceFocusLost(evt);
            }
        });
        txtBirthPlace.addKeyListener(new java.awt.event.KeyAdapter() {
            public void keyPressed(java.awt.event.KeyEvent evt) {
                txtBirthPlaceKeyPressed(evt);
            }
        });

        txtBirthDate.setFont(new java.awt.Font("Tahoma", 0, 12)); // NOI18N
        txtBirthDate.setBorder(javax.swing.BorderFactory.createTitledBorder("ΗΜ/ΝΙΑ ΓΕΝΝΗΣΗΣ"));
        txtBirthDate.setEnabled(false);
        txtBirthDate.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                txtBirthDateActionPerformed(evt);
            }
        });
        txtBirthDate.addFocusListener(new java.awt.event.FocusAdapter() {
            public void focusGained(java.awt.event.FocusEvent evt) {
                txtBirthDateFocusGained(evt);
            }
            public void focusLost(java.awt.event.FocusEvent evt) {
                txtBirthDateFocusLost(evt);
            }
        });
        txtBirthDate.addKeyListener(new java.awt.event.KeyAdapter() {
            public void keyPressed(java.awt.event.KeyEvent evt) {
                txtBirthDateKeyPressed(evt);
            }
        });

        cboxMusicGenre.setFont(new java.awt.Font("Tahoma", 0, 12)); // NOI18N
        cboxMusicGenre.setMaximumRowCount(4);
        cboxMusicGenre.setBorder(javax.swing.BorderFactory.createTitledBorder("ΕΙΔΟΣ ΜΟΥΣΙΚΗΣ"));
        cboxMusicGenre.setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR));
        cboxMusicGenre.setEnabled(false);
        cboxMusicGenre.setMinimumSize(new java.awt.Dimension(80, 40));
        cboxMusicGenre.setPreferredSize(new java.awt.Dimension(80, 42));

        org.jdesktop.swingbinding.JComboBoxBinding jComboBoxBinding = org.jdesktop.swingbinding.SwingBindings.createJComboBoxBinding(org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ_WRITE, listMusicGenre, cboxMusicGenre);
        bindingGroup.addBinding(jComboBoxBinding);

        cboxMusicGenre.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                cboxMusicGenreActionPerformed(evt);
            }
        });
        cboxMusicGenre.addKeyListener(new java.awt.event.KeyAdapter() {
            public void keyPressed(java.awt.event.KeyEvent evt) {
                cboxMusicGenreKeyPressed(evt);
            }
        });

        cboxSex.setFont(new java.awt.Font("Tahoma", 0, 12)); // NOI18N
        cboxSex.setMaximumRowCount(3);
        cboxSex.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "?", "ΘΗΛΥ", "ΑΡΡΕΝ" }));
        cboxSex.setSelectedIndex(-1);
        cboxSex.setBorder(javax.swing.BorderFactory.createTitledBorder("ΦΥΛΟ"));
        cboxSex.setEnabled(false);
        cboxSex.setMinimumSize(new java.awt.Dimension(80, 40));
        cboxSex.setPreferredSize(new java.awt.Dimension(80, 40));
        cboxSex.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                cboxSexActionPerformed(evt);
            }
        });
        cboxSex.addKeyListener(new java.awt.event.KeyAdapter() {
            public void keyPressed(java.awt.event.KeyEvent evt) {
                cboxSexKeyPressed(evt);
            }
        });

        javax.swing.GroupLayout jPanelFORMLayout = new javax.swing.GroupLayout(jPanelFORM);
        jPanelFORM.setLayout(jPanelFORMLayout);
        jPanelFORMLayout.setHorizontalGroup(
            jPanelFORMLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanelFORMLayout.createSequentialGroup()
                .addContainerGap()
                .addGroup(jPanelFORMLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(jPanelFORMLayout.createSequentialGroup()
                        .addComponent(txtArtisticName, javax.swing.GroupLayout.PREFERRED_SIZE, 450, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addGap(18, 18, 18)
                        .addComponent(cboxMusicGenre, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                    .addGroup(jPanelFORMLayout.createSequentialGroup()
                        .addGroup(jPanelFORMLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
                            .addGroup(jPanelFORMLayout.createSequentialGroup()
                                .addComponent(txtFirstName, javax.swing.GroupLayout.PREFERRED_SIZE, 230, javax.swing.GroupLayout.PREFERRED_SIZE)
                                .addGap(18, 18, 18)
                                .addComponent(txtLastName))
                            .addComponent(txtBirthPlace, javax.swing.GroupLayout.PREFERRED_SIZE, 540, javax.swing.GroupLayout.PREFERRED_SIZE))
                        .addGap(18, 18, 18)
                        .addGroup(jPanelFORMLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addGroup(jPanelFORMLayout.createSequentialGroup()
                                .addGap(0, 36, Short.MAX_VALUE)
                                .addComponent(cboxSex, javax.swing.GroupLayout.PREFERRED_SIZE, 100, javax.swing.GroupLayout.PREFERRED_SIZE))
                            .addComponent(txtBirthDate))))
                .addGap(10, 10, 10))
        );
        jPanelFORMLayout.setVerticalGroup(
            jPanelFORMLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanelFORMLayout.createSequentialGroup()
                .addContainerGap()
                .addGroup(jPanelFORMLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(txtArtisticName, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(cboxMusicGenre, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addGap(18, 18, 18)
                .addGroup(jPanelFORMLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(txtFirstName, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(txtLastName, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(cboxSex, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addGap(18, 18, 18)
                .addGroup(jPanelFORMLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(txtBirthPlace, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(txtBirthDate, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );

        getContentPane().add(jPanelFORM);
        jPanelFORM.setBounds(40, 300, 720, 180);

        jLabelARTISTmale.setIcon(new javax.swing.ImageIcon(getClass().getResource("/misc/design/~ArtistMale.png"))); // NOI18N
        jLabelARTISTmale.setVerticalAlignment(javax.swing.SwingConstants.TOP);
        jLabelARTISTmale.setEnabled(false);
        jLabelARTISTmale.setFocusable(false);
        jLabelARTISTmale.setMaximumSize(new java.awt.Dimension(0, 0));
        jLabelARTISTmale.setMinimumSize(new java.awt.Dimension(0, 0));
        getContentPane().add(jLabelARTISTmale);
        jLabelARTISTmale.setBounds(85, 170, 120, 120);

        jLabelARTISTfemale.setIcon(new javax.swing.ImageIcon(getClass().getResource("/misc/design/~ArtistFemale.png"))); // NOI18N
        jLabelARTISTfemale.setVerticalAlignment(javax.swing.SwingConstants.TOP);
        jLabelARTISTfemale.setEnabled(false);
        jLabelARTISTfemale.setFocusable(false);
        jLabelARTISTfemale.setMaximumSize(new java.awt.Dimension(0, 0));
        jLabelARTISTfemale.setMinimumSize(new java.awt.Dimension(0, 0));
        getContentPane().add(jLabelARTISTfemale);
        jLabelARTISTfemale.setBounds(80, 170, 120, 120);

        jButtonRETURN.setFont(new java.awt.Font("Tahoma", 0, 12)); // NOI18N
        jButtonRETURN.setIcon(new javax.swing.ImageIcon(getClass().getResource("/misc/design/_Return.png"))); // NOI18N
        jButtonRETURN.setText("Επιστροφή");
        jButtonRETURN.setBorder(new javax.swing.border.SoftBevelBorder(javax.swing.border.BevelBorder.RAISED));
        jButtonRETURN.setFocusable(false);
        jButtonRETURN.setIconTextGap(8);
        jButtonRETURN.setMargin(new java.awt.Insets(2, 0, 2, 0));
        jButtonRETURN.setMaximumSize(new java.awt.Dimension(100, 30));
        jButtonRETURN.setMinimumSize(new java.awt.Dimension(100, 30));
        jButtonRETURN.setPreferredSize(new java.awt.Dimension(110, 30));
        jButtonRETURN.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButtonRETURNActionPerformed(evt);
            }
        });
        getContentPane().add(jButtonRETURN);
        jButtonRETURN.setBounds(40, 541, 110, 30);

        jButtonADD.setFont(new java.awt.Font("Tahoma", 0, 12)); // NOI18N
        jButtonADD.setIcon(new javax.swing.ImageIcon(getClass().getResource("/misc/design/_Add.png"))); // NOI18N
        jButtonADD.setText("Εισαγωγή");
        jButtonADD.setBorder(new javax.swing.border.SoftBevelBorder(javax.swing.border.BevelBorder.RAISED));
        jButtonADD.setFocusable(false);
        jButtonADD.setIconTextGap(12);
        jButtonADD.setInheritsPopupMenu(true);
        jButtonADD.setMargin(new java.awt.Insets(2, 0, 2, 0));
        jButtonADD.setMaximumSize(new java.awt.Dimension(100, 30));
        jButtonADD.setMinimumSize(new java.awt.Dimension(100, 30));
        jButtonADD.setPreferredSize(new java.awt.Dimension(100, 30));
        jButtonADD.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButtonADDActionPerformed(evt);
            }
        });
        getContentPane().add(jButtonADD);
        jButtonADD.setBounds(420, 243, 100, 30);

        jButtonEDIT.setFont(new java.awt.Font("Tahoma", 0, 12)); // NOI18N
        jButtonEDIT.setIcon(new javax.swing.ImageIcon(getClass().getResource("/misc/design/_Edit.png"))); // NOI18N
        jButtonEDIT.setText("Επεξεργασία");
        jButtonEDIT.setBorder(new javax.swing.border.SoftBevelBorder(javax.swing.border.BevelBorder.RAISED));
        jButtonEDIT.setFocusable(false);
        jButtonEDIT.setIconTextGap(12);
        jButtonEDIT.setInheritsPopupMenu(true);
        jButtonEDIT.setMargin(new java.awt.Insets(2, 0, 2, 0));
        jButtonEDIT.setMaximumSize(new java.awt.Dimension(120, 30));
        jButtonEDIT.setMinimumSize(new java.awt.Dimension(120, 30));
        jButtonEDIT.setPreferredSize(new java.awt.Dimension(120, 30));
        jButtonEDIT.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButtonEDITActionPerformed(evt);
            }
        });
        getContentPane().add(jButtonEDIT);
        jButtonEDIT.setBounds(530, 243, 120, 30);

        jButtonREMOVE.setFont(new java.awt.Font("Tahoma", 0, 12)); // NOI18N
        jButtonREMOVE.setIcon(new javax.swing.ImageIcon(getClass().getResource("/misc/design/_Remove.png"))); // NOI18N
        jButtonREMOVE.setText("Διαγραφή");
        jButtonREMOVE.setBorder(new javax.swing.border.SoftBevelBorder(javax.swing.border.BevelBorder.RAISED));
        jButtonREMOVE.setFocusable(false);
        jButtonREMOVE.setIconTextGap(12);
        jButtonREMOVE.setInheritsPopupMenu(true);
        jButtonREMOVE.setMargin(new java.awt.Insets(2, 0, 2, 0));
        jButtonREMOVE.setMaximumSize(new java.awt.Dimension(100, 30));
        jButtonREMOVE.setMinimumSize(new java.awt.Dimension(100, 30));
        jButtonREMOVE.setPreferredSize(new java.awt.Dimension(100, 30));
        jButtonREMOVE.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButtonREMOVEActionPerformed(evt);
            }
        });
        getContentPane().add(jButtonREMOVE);
        jButtonREMOVE.setBounds(660, 243, 100, 30);

        jButtonSAVE.setFont(new java.awt.Font("Tahoma", 0, 12)); // NOI18N
        jButtonSAVE.setIcon(new javax.swing.ImageIcon(getClass().getResource("/misc/design/_Check.png"))); // NOI18N
        jButtonSAVE.setText("Αποθήκευση");
        jButtonSAVE.setBorder(new javax.swing.border.SoftBevelBorder(javax.swing.border.BevelBorder.RAISED));
        jButtonSAVE.setEnabled(false);
        jButtonSAVE.setFocusable(false);
        jButtonSAVE.setIconTextGap(12);
        jButtonSAVE.setInheritsPopupMenu(true);
        jButtonSAVE.setMargin(new java.awt.Insets(2, 0, 2, 0));
        jButtonSAVE.setMaximumSize(new java.awt.Dimension(120, 30));
        jButtonSAVE.setMinimumSize(new java.awt.Dimension(120, 30));
        jButtonSAVE.setPreferredSize(new java.awt.Dimension(120, 30));
        jButtonSAVE.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButtonSAVEActionPerformed(evt);
            }
        });
        getContentPane().add(jButtonSAVE);
        jButtonSAVE.setBounds(530, 490, 120, 30);

        jButtonCANCEL.setFont(new java.awt.Font("Tahoma", 0, 12)); // NOI18N
        jButtonCANCEL.setIcon(new javax.swing.ImageIcon(getClass().getResource("/misc/design/_Cancel.png"))); // NOI18N
        jButtonCANCEL.setText("Ακύρωση");
        jButtonCANCEL.setBorder(new javax.swing.border.SoftBevelBorder(javax.swing.border.BevelBorder.RAISED));
        jButtonCANCEL.setEnabled(false);
        jButtonCANCEL.setFocusable(false);
        jButtonCANCEL.setIconTextGap(12);
        jButtonCANCEL.setInheritsPopupMenu(true);
        jButtonCANCEL.setMargin(new java.awt.Insets(2, 0, 2, 0));
        jButtonCANCEL.setMaximumSize(new java.awt.Dimension(100, 30));
        jButtonCANCEL.setMinimumSize(new java.awt.Dimension(100, 30));
        jButtonCANCEL.setPreferredSize(new java.awt.Dimension(100, 30));
        jButtonCANCEL.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButtonCANCELActionPerformed(evt);
            }
        });
        getContentPane().add(jButtonCANCEL);
        jButtonCANCEL.setBounds(660, 490, 100, 30);

        jLabelBACKGROUND.setIcon(new javax.swing.ImageIcon(getClass().getResource("/misc/design/MICROPHONE_maxi.png"))); // NOI18N
        jLabelBACKGROUND.setFocusable(false);
        jLabelBACKGROUND.setPreferredSize(new java.awt.Dimension(800, 576));
        getContentPane().add(jLabelBACKGROUND);
        jLabelBACKGROUND.setBounds(0, 0, 800, 576);

        bindingGroup.bind();

        pack();
    }// </editor-fold>//GEN-END:initComponents

    private void txtArtisticNameActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_txtArtisticNameActionPerformed
        System.out.println("  ~ActionPerformed artisticname");
        // αν υπάρχουν ανεπιθύμητοι \ χαρακτήρες, grab focus και warningBox
        if(!mm.getExpertMode() && !txtArtisticName.getText().equals(txtArtisticName.getText().replace("\\",""))) {
            txtArtisticName.grabFocus(); rss.infoBox(this, "Δεν επιτρέπονται  \\  σε αλφαριθμητικά"); return;
          // αλλιώς μορφοποίησε το αλφαριθμητικό και σιωπηρά αφαίρεσε τα ανεπιθύμητα \ αν είμαστε σε ExpertMode 
        } else txtArtisticName.setText(txtArtisticName.getText().replace("\\","").trim());
        // ενδεχόμενος περιορισμός αλφαριθμητικού πεδίου στο μέγεθος που έχει δηλωθεί στο αντίστοιχο POJO
        if(txtArtisticName.getText().length() > rss.fieldLength(this, Artist.class, "artisticname")) {
            txtArtisticName.setText(txtArtisticName.getText().substring(0, rss.fieldLength(this, Artist.class, "artisticname"))); }
        tempTxt = txtArtisticName.getText();  // μετά τις μορφοποιήσεις επικαιροποίησε την πρότερη κατάσταση
        // αν η κλήση της μεθόδου δεν προέρχεται από κάποιο focus lost, highlighted επιλογή πεδίου
        if(evt != null) { System.out.println("   ~selectAll"); txtArtisticName.selectAll(); }
    }//GEN-LAST:event_txtArtisticNameActionPerformed

    private void txtArtisticNameFocusLost(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_txtArtisticNameFocusLost
        System.out.println("  ~FocusLost artisticname");
        if(editStatus && !evt.isTemporary()) {  // αν όχι προσωρινό focus lost
            txtArtisticNameActionPerformed(null); fieldFocused = 0;  // έλεγχος εγκυρότητας αλφαριθμητικού μέσω txtArtisticNameActionPerformed
            System.out.println("   ~gotoActionPerformed & focused:0");
        }
    }//GEN-LAST:event_txtArtisticNameFocusLost

    private void txtArtisticNameFocusGained(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_txtArtisticNameFocusGained
        System.out.println("  ~FocusGained artisticname ");
        if(fieldFocused == 0) {  // αν δεν πρόκειται για ήδη focused πεδίο, σώσε το προσωρινά και highlighted επιλογή
            tempTxt = txtArtisticName.getText(); txtArtisticName.selectAll(); fieldFocused = 1;
            System.out.println("   ~selectAll & focused:1"); 
        }
    }//GEN-LAST:event_txtArtisticNameFocusGained

    private void txtArtisticNameKeyPressed(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_txtArtisticNameKeyPressed
        if(evt.getKeyCode() == 27) {  // αν πατηθεί Escape επαναφορά στην προηγούμενη έγκυρη κατάσταση προσέχοντας και πιθανά nulls
            // αν συνέβησαν αλλαγές στο πεδίο και δεν είναι ήδη ολόκληρο επιλεγμένο
            if(!tempTxt.equals(txtArtisticName.getText()) || !tempTxt.equals(txtArtisticName.getSelectedText() == null? "" : txtArtisticName.getSelectedText())) { 
                // επαναφορά προηγούμενου και highlighted επιλογή
                txtArtisticName.setText(tempTxt); txtArtisticName.selectAll(); 
            } else jButtonCANCELActionPerformed(null);  // αλλιώς έξοδος από τη φόρμα επεξεργασίας
        }
    }//GEN-LAST:event_txtArtisticNameKeyPressed

    private void txtFirstNameActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_txtFirstNameActionPerformed
        System.out.println("  ~ActionPerformed firstname");
        // αν υπάρχουν ανεπιθύμητοι \ χαρακτήρες, grab focus και warningBox
        if(!mm.getExpertMode() && !txtFirstName.getText().equals(txtFirstName.getText().replace("\\",""))) {
            txtFirstName.grabFocus(); rss.infoBox(this, "Δεν επιτρέπονται  \\  σε αλφαριθμητικά"); return;
          // αλλιώς μορφοποίησε το αλφαριθμητικό και σιωπηρά αφαίρεσε τα ανεπιθύμητα \ αν είμαστε σε ExpertMode 
        } else txtFirstName.setText(txtFirstName.getText().replace("\\","").trim());
        // ενδεχόμενος περιορισμός αλφαριθμητικού πεδίου στο μέγεθος που έχει δηλωθεί στο αντίστοιχο POJO
        if(txtFirstName.getText().length() > rss.fieldLength(this, Artist.class, "firstname")) {
            txtFirstName.setText(txtFirstName.getText().substring(0, rss.fieldLength(this, Artist.class, "firstname"))); }
        tempTxt = txtFirstName.getText();  // μετά τις μορφοποιήσεις επικαιροποίησε την πρότερη κατάσταση
        // αν η κλήση της μεθόδου δεν προέρχεται από κάποιο focus lost, highlighted επιλογή πεδίου
        if(evt != null) { System.out.println("   ~selectAll"); txtFirstName.selectAll(); }
    }//GEN-LAST:event_txtFirstNameActionPerformed

    private void txtFirstNameFocusLost(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_txtFirstNameFocusLost
        System.out.println("  ~FocusLost firstname");
        
        if(editStatus && !evt.isTemporary()) {  // αν όχι προσωρινό focus lost
            txtFirstNameActionPerformed(null); fieldFocused = 0;  // έλεγχος εγκυρότητας αλφαριθμητικού μέσω txtFirstNameActionPerformed
            System.out.println("   ~gotoActionPerformed & focused:0");
        }
    }//GEN-LAST:event_txtFirstNameFocusLost

    private void txtFirstNameFocusGained(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_txtFirstNameFocusGained
        System.out.println("  ~FocusGained firstname " + fieldFocused);
        if(fieldFocused == 0) {  // αν δεν πρόκειται για ήδη focused πεδίο, σώσε το προσωρινά και highlighted επιλογή
            tempTxt = txtFirstName.getText(); txtFirstName.selectAll(); fieldFocused = 2;
            System.out.println("   ~selectAll & focused:2"); 
        }
    }//GEN-LAST:event_txtFirstNameFocusGained

    private void txtFirstNameKeyPressed(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_txtFirstNameKeyPressed
        if(evt.getKeyCode() == 27) {  // αν πατηθεί Escape επαναφορά στην προηγούμενη έγκυρη κατάσταση προσέχοντας και πιθανά nulls
            // αν συνέβησαν αλλαγές στο πεδίο και δεν είναι ήδη ολόκληρο επιλεγμένο
            if(!tempTxt.equals(txtFirstName.getText()) || !tempTxt.equals(txtFirstName.getSelectedText() == null? "" : txtFirstName.getSelectedText())) { 
                // επαναφορά προηγούμενου και highlighted επιλογή
                txtFirstName.setText(tempTxt); txtFirstName.selectAll();
            } else jButtonCANCELActionPerformed(null);  // αλλιώς έξοδος από τη φόρμα επεξεργασίας
        }
    }//GEN-LAST:event_txtFirstNameKeyPressed

    private void txtLastNameActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_txtLastNameActionPerformed
        System.out.println("  ~ActionPerformed lastname");
        // υπάρχουν ανεπιθύμητοι \ χαρακτήρες, grab focus και warningBox
        if(!mm.getExpertMode() && !txtLastName.getText().equals(txtLastName.getText().replace("\\",""))) {
            txtLastName.grabFocus(); rss.infoBox(this, "Δεν επιτρέπονται  \\  σε αλφαριθμητικά"); return;
          // αλλιώς μορφοποίησε το αλφαριθμητικό και σιωπηρά αφαίρεσε τα ανεπιθύμητα \ αν είμαστε σε ExpertMode 
        } else txtLastName.setText(txtLastName.getText().replace("\\","").trim());
        // ενδεχόμενος περιορισμός αλφαριθμητικού πεδίου στο μέγεθος που έχει δηλωθεί στο αντίστοιχο POJO
        if(txtLastName.getText().length() > rss.fieldLength(this, Artist.class, "lastname")) {
            txtLastName.setText(txtLastName.getText().substring(0, rss.fieldLength(this, Artist.class, "lastname"))); }
        tempTxt = txtLastName.getText();  // μετά τις μορφοποιήσεις επικαιροποίησε την πρότερη κατάσταση
        // αν η κλήση της μεθόδου δεν προέρχεται από κάποιο focus lost, highlighted επιλογή πεδίου
        if(evt != null) { System.out.println("   ~selectAll"); txtLastName.selectAll(); }
    }//GEN-LAST:event_txtLastNameActionPerformed

    private void txtLastNameFocusLost(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_txtLastNameFocusLost
        System.out.println("  ~FocusLost lastname");
        if(editStatus && !evt.isTemporary()) {  // αν όχι προσωρινό focus lost
            txtLastNameActionPerformed(null); fieldFocused = 0;  // έλεγχος εγκυρότητας αλφαριθμητικού μέσω txtLastNameActionPerformed
            System.out.println("   ~gotoActionPerformed & focused:0");
        }
    }//GEN-LAST:event_txtLastNameFocusLost

    private void txtLastNameFocusGained(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_txtLastNameFocusGained
        System.out.println("  ~FocusGained lastname " + fieldFocused);
        if(fieldFocused == 0) {  // αν δεν πρόκειται για ήδη focused πεδίο, σώσε το προσωρινά και highlighted επιλογή
            tempTxt = txtLastName.getText(); txtLastName.selectAll(); fieldFocused = 3;
            System.out.println("   ~selectAll & focused:3"); 
        }
    }//GEN-LAST:event_txtLastNameFocusGained

    private void txtLastNameKeyPressed(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_txtLastNameKeyPressed
        if(evt.getKeyCode() == 27) {  // αν πατηθεί Escape επαναφορά στην προηγούμενη έγκυρη κατάσταση προσέχοντας και πιθανά nulls
            // αν συνέβησαν αλλαγές στο πεδίο και δεν είναι ήδη ολόκληρο επιλεγμένο
            if(!tempTxt.equals(txtLastName.getText()) || !tempTxt.equals(txtLastName.getSelectedText() == null? "" : txtLastName.getSelectedText())) { 
                // επαναφορά προηγούμενου και highlighted επιλογή
                txtLastName.setText(tempTxt); txtLastName.selectAll();
            } else jButtonCANCELActionPerformed(null);  // αλλιώς έξοδος από τη φόρμα επεξεργασίας
        }
    }//GEN-LAST:event_txtLastNameKeyPressed

    private void txtBirthPlaceActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_txtBirthPlaceActionPerformed
        System.out.println("  ~ActionPerformed birthplace");
        // αν υπάρχουν ανεπιθύμητοι \ χαρακτήρες, grab focus και warningBox
        if(!mm.getExpertMode() && !txtBirthPlace.getText().equals(txtBirthPlace.getText().replace("\\",""))) {
            txtBirthPlace.grabFocus(); rss.infoBox(this, "Δεν επιτρέπονται  \\  σε αλφαριθμητικά"); return;
          // αλλιώς μορφοποίησε το αλφαριθμητικό και σιωπηρά αφαίρεσε τα ανεπιθύμητα \ αν είμαστε σε ExpertMode 
        } else txtBirthPlace.setText(txtBirthPlace.getText().replace("\\","").trim());
        // ενδεχόμενος περιορισμός αλφαριθμητικού πεδίου στο μέγεθος που έχει δηλωθεί στο αντίστοιχο POJO
        if(txtBirthPlace.getText().length() > rss.fieldLength(this, Artist.class, "birthplace")) {
            txtBirthPlace.setText(txtBirthPlace.getText().substring(0, rss.fieldLength(this, Artist.class, "birthplace"))); }
        tempTxt = txtBirthPlace.getText();  // μετά τις μορφοποιήσεις επικαιροποίησε την πρότερη κατάσταση
        // αν η κλήση της μεθόδου δεν προέρχεται από κάποιο focus lost, highlighted επιλογή πεδίου
        if(evt != null) { System.out.println("   ~selectAll"); txtBirthPlace.selectAll(); }
    }//GEN-LAST:event_txtBirthPlaceActionPerformed

    private void txtBirthPlaceFocusLost(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_txtBirthPlaceFocusLost
        System.out.println("  ~FocusLost birthplace");
        if(editStatus && !evt.isTemporary()) {  // αν όχι προσωρινό focus lost
            txtBirthPlaceActionPerformed(null); fieldFocused = 0;  // έλεγχος εγκυρότητας αλφαριθμητικού μέσω txtBirthPlaceActionPerformed
            System.out.println("   ~gotoActionPerformed & focused:0");
        }
    }//GEN-LAST:event_txtBirthPlaceFocusLost

    private void txtBirthPlaceFocusGained(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_txtBirthPlaceFocusGained
        System.out.println("  ~FocusGained birthplace " + fieldFocused);
        if(fieldFocused == 0) {  // αν δεν πρόκειται για ήδη focused πεδίο, σώσε το προσωρινά και highlighted επιλογή
            tempTxt = txtBirthPlace.getText(); txtBirthPlace.selectAll(); fieldFocused = 4;
            System.out.println("   ~selectAll & focused:4"); 
        }
    }//GEN-LAST:event_txtBirthPlaceFocusGained

    private void txtBirthPlaceKeyPressed(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_txtBirthPlaceKeyPressed
        if(evt.getKeyCode() == 27) {  // αν πατηθεί Escape επαναφορά στην προηγούμενη έγκυρη κατάσταση προσέχοντας και πιθανά nulls
            // αν συνέβησαν αλλαγές στο πεδίο και δεν είναι ήδη ολόκληρο επιλεγμένο
            if(!tempTxt.equals(txtBirthPlace.getText()) || !tempTxt.equals(txtBirthPlace.getSelectedText() == null? "" : txtBirthPlace.getSelectedText())) { 
                // επαναφορά προηγούμενου και highlighted επιλογή
                txtBirthPlace.setText(tempTxt); txtBirthPlace.selectAll();
            } else jButtonCANCELActionPerformed(null);  // αλλιώς έξοδος από τη φόρμα επεξεργασίας
        }
    }//GEN-LAST:event_txtBirthPlaceKeyPressed

    private void txtBirthDateActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_txtBirthDateActionPerformed
        System.out.println("  ~ActionPerformed birthdate");
        if(!mm.getExpertMode() && rss.validateDate(txtBirthDate.getText(), dateRenderer) == null) {  // έλεγχος εγκυρότητας ημερομηνίας
            // όχι επιτρεπτή ημερομηνία, grab focus και warningBox
            txtBirthDate.grabFocus(); rss.infoBox(this, "Η ημερομηνία δεν είναι έγκυρη"); return;
          // αλλιώς μορφοποίησε την καινούρια ημερομηνία κατά το δοθέν πρότυπο ή σιωπηρά empty string αν είμαστε σε ExpertMode και δεν είναι έγκυρη
        } else txtBirthDate.setText(rss.validateDate(txtBirthDate.getText(), dateRenderer) == null? "" : rss.validateDate(txtBirthDate.getText(), dateRenderer));
        tempTxt = txtBirthDate.getText();  // μετά τους ελέγχους και μορφοποιήσεις επικαιροποίησε την πρότερη κατάσταση
        // αν η κλήση της μεθόδου δεν προέρχεται από κάποιο focus lost, highlighted επιλογή πεδίου
        if(evt != null) { System.out.println("   ~selectAll"); txtBirthDate.selectAll(); }
    }//GEN-LAST:event_txtBirthDateActionPerformed

    private void txtBirthDateFocusLost(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_txtBirthDateFocusLost
        System.out.println("  ~FocusLost birthdate");
        if(editStatus && !evt.isTemporary()) {  // αν όχι προσωρινό focus lost
            txtBirthDateActionPerformed(null); fieldFocused = 0;  // έλεγχος εγκυρότητας αλφαριθμητικού μέσω txtBirthDateActionPerformed
            System.out.println("   ~gotoActionPerformed & focused:0");
        }
    }//GEN-LAST:event_txtBirthDateFocusLost

    private void txtBirthDateFocusGained(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_txtBirthDateFocusGained
        System.out.println("  ~FocusGained birthdate " + fieldFocused);
        if(fieldFocused == 0) {  // αν δεν πρόκειται για ήδη focused πεδίο, σώσε το προσωρινά και highlighted επιλογή
            tempTxt = txtBirthDate.getText(); txtBirthDate.selectAll(); fieldFocused = 5;
            System.out.println("   ~selectAll & focused:5"); 
        }
    }//GEN-LAST:event_txtBirthDateFocusGained

    private void txtBirthDateKeyPressed(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_txtBirthDateKeyPressed
        if(evt.getKeyCode() == 27) {  // αν πατηθεί Escape επαναφορά στην προηγούμενη έγκυρη κατάσταση πρόσέχοντας και πιθανά nulls
            // αν συνέβησαν αλλαγές στο πεδίο και δεν είναι ήδη ολόκληρο επιλεγμένο
            if(!tempTxt.equals(txtBirthDate.getText()) || !tempTxt.equals(txtBirthDate.getSelectedText() == null? "" : txtBirthDate.getSelectedText())) { 
                // επαναφορά προηγούμενου και highlighted επιλογή
                txtBirthDate.setText(tempTxt); txtBirthDate.selectAll();
            } else jButtonCANCELActionPerformed(null);  // αλλιώς έξοδος από τη φόρμα επεξεργασίας
        }
    }//GEN-LAST:event_txtBirthDateKeyPressed
   
    private void formWindowOpened(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_formWindowOpened
        ClearAllFields();  // καθαρισμός όλων των πεδίων της φόρμας επεξεργασίας
        System.out.println("~ArtistManagement is ON");
    }//GEN-LAST:event_formWindowOpened

    private void formWindowGainedFocus(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_formWindowGainedFocus
        // κάθε φορά που επικεντρώνω στο παράθυρο επικαιροποιώ και την επικεφαλίδα του
        if(mm.getExpertMode()) this.setTitle(frameTitle + " (ExpertMode)"); else this.setTitle(frameTitle);
    }//GEN-LAST:event_formWindowGainedFocus

    private void jButtonRETURNActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonRETURNActionPerformed
        System.out.println(" ~ButtonReturn");
        if(editStatus) {
            editStatus = false;  // για προστασία των πεδίων από τυχόν αλλαγές
            // αν έχουν συμβεί αλλαγές και δεν είμαστε σε ExpertMode, επιλογή okcancelBox για επιστροφή στη φόρμα 
            if(!compareAllFields() && !mm.getExpertMode() && !(rss.okcancelBox(this, "Οι αλλαγές θα χαθούν. Είστε σίγουροι; ") == 0)) {editStatus = true; return; }
        }    
        this.toBack(); dispose();  // στείλε το παράθυρο στο background και κλείσε το ArtistManagement
        System.out.println(" ~closing ArtistManagement by Button");
    }//GEN-LAST:event_jButtonRETURNActionPerformed

    private void formWindowClosing(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_formWindowClosing
        System.out.println(" ~ButtonX");
        if(editStatus) {
            editStatus = false;  // για προστασία των πεδίων από τυχόν αλλαγές
            // αν έχουν συμβεί αλλαγές και δεν είμαστε σε ExpertMode, επιλογή okcancelBox για επιστροφή στη φόρμα 
            if(!compareAllFields() && !mm.getExpertMode() && !(rss.okcancelBox(this, "Οι αλλαγές θα χαθούν. Είστε σίγουροι; ") == 0)) {editStatus = true; return; }
        }
        this.toBack(); dispose();  // στείλε το παράθυρο στο background και κλείσε το ArtistManagement
        System.out.println(" ~closing ArtistManagement by X");
    }//GEN-LAST:event_formWindowClosing

    private void formWindowClosed(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_formWindowClosed
        mm.setEnabled(true);  // έξοδος και ενεργοποίηση της MainMenu
        System.out.println("~ArtistManagement is now OFF");
    }//GEN-LAST:event_formWindowClosed

    private void jLabelMAINTABLEMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_jLabelMAINTABLEMouseClicked
        if(!editStatus) ArtistTABLE.getRowSorter().setSortKeys(null);  // κατάργηση ταξινόμησης
        // εμφάνιση viewport που να περιέχει την επιλεγμένη γραμμή και επικαιροποίηση του πίνακα
        ArtistTABLE.scrollRectToVisible(ArtistTABLE.getCellRect(ArtistTABLE.getSelectedRow(), 0, true));
        ArtistTABLE.repaint();
    }//GEN-LAST:event_jLabelMAINTABLEMouseClicked

    private void ArtistTABLEKeyPressed(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_ArtistTABLEKeyPressed
        //System.out.println(evt.getKeyCode());
        if(evt.getKeyCode() == 32) { evt.setKeyCode(10); }  // επαναχαρτογράφηση Space key στο Enter key
        else if(evt.getKeyCode() == 127) jButtonREMOVEActionPerformed(null);  // ανακατεύθυνση του Delete key
        else if(evt.getKeyCode() == 10) jButtonEDITActionPerformed(null);  // ανακατεύθυνση του Enter key
        ArtistTABLEKeyReleased(null);  // ίδιος κώδικας με ArtistTABLEKeyReleased
    }//GEN-LAST:event_ArtistTABLEKeyPressed

    private void ArtistTABLEKeyReleased(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_ArtistTABLEKeyReleased
        int row = ArtistTABLE.getSelectedRow();
        // έλεγχος επιλογής γραμμής πίνακα Καλλιτεχνών
        if(row != -1) {  // αν ο πίνακας δεν είναι άδειος
            // αντιστοίχιση a τύπου Artist στην επιλεγμένη γραμμή
            a = listArtist.get(ArtistTABLE.convertRowIndexToModel(row));
            UpdateAllFields();  // ενημέρωση πεδίων φόρμας επεξεργασίας με τις τιμές του επιλεγμένου αντικειμένου
        } else {
            ArtistTABLE.clearSelection(); ClearAllFields();  // clear επιλογής και πεδίων
        }
    }//GEN-LAST:event_ArtistTABLEKeyReleased

    private void ArtistTABLEMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_ArtistTABLEMouseClicked
        if(!editStatus) {
            // εξυπηρέτηση single click
            if(evt.getClickCount() == 1) { /*ArtistTABLEKeyReleased(null);*/ }  // τίποτα ή εναλλακτικά ίδιος κώδικας με ArtistTABLEKeyReleased
            // εξυπηρέτηση double click με μετάβαση σε κατάσταση επεξεργασίας
            else if(evt.getClickCount() == 2) jButtonEDITActionPerformed(null);
        }
    }//GEN-LAST:event_ArtistTABLEMouseClicked

    private void ArtistTABLEMouseReleased(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_ArtistTABLEMouseReleased
        if(!editStatus) {
            ArtistTABLEKeyReleased(null);  // ίδιος κώδικας με ArtistTABLEKeyReleased
        }
    }//GEN-LAST:event_ArtistTABLEMouseReleased

    private void jPanelFORMKeyPressed(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_jPanelFORMKeyPressed
        if(evt.getKeyCode() == 27) jButtonCANCELActionPerformed(null);  // έξοδος από το jPanel με Escape
    }//GEN-LAST:event_jPanelFORMKeyPressed

    private void jButtonADDActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonADDActionPerformed
        if(!editStatus) {  // αν όχι σε κατάστασης επεξεργασίας
            System.out.println(" ~ButtonADD");
            // reset combo boxes to top (το index -1 είναι το null) και καθαρισμός πεδίων
            cboxMusicGenre.setSelectedIndex(0); cboxSex.setSelectedIndex(0); ClearAllFields();
            // προετοιμασία ενεργών & ανενεργών components για μετάβαση σε κατάσταση επεξεργασίας
            EnableDisableFields(true); jButtonADD.setEnabled(true); jPanelFORM.grabFocus();
            // νέος a τύπου Artist και κατάσταση επεξεργασίας ADD
            a = new Artist(); addORedit = true;
            //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
        } else Toolkit.getDefaultToolkit().beep();
    }//GEN-LAST:event_jButtonADDActionPerformed

    private void jButtonEDITActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonEDITActionPerformed
        if(!editStatus) {  // αν όχι σε κατάσταση επεξεργασίας
            System.out.println(" ~ButtonEDIT");
            if(ArtistTABLE.getSelectedRow() != -1) {  // και ο πίνακας δεν είναι άδειος
                int row = ArtistTABLE.getSelectedRow();  // αποθήκευση επιλεγμένης γραμμής
                // αντιστοίχιση αντικειμένου a τύπου Artist στην επιλεγμένη γραμμή, προσωρινή αποθήκευση 
                a = listArtist.get(ArtistTABLE.convertRowIndexToModel(row)); CopyAllFields(tempA, a);
                if(rss.accessDB(this, a,"R")) {  // και έλεγχος/επικαιροποίηση των πεδίων του αντικειμένου a από την ΒΔ as good measure
                    // αν όλα πήγαν καλά, προετοιμασία ενεργών & ανενεργών components
                    EnableDisableFields(true); jButtonEDIT.setEnabled(true); jPanelFORM.grabFocus();
                    // νέα προσωρινή αποθήκευση, ενημέρωση πεδίων με τις τιμές του πίνακα και κατάσταση επεξεργασίας EDIT
                    CopyAllFields(tempA, a); UpdateAllFields(); addORedit = false;
                    //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
                } else {  // αν όμως συμβεί σφάλμά κατά τον έλεγχος/επικαιροποίηση των πεδίων του αντικειμένου a από την ΒΔ
                    // εκ νέου αντιστοίχιση αντικειμένου a τύπου Artist στην επιλεγμένη γραμμή και επαναφορά
                    a = listArtist.get(ArtistTABLE.convertRowIndexToModel(row)); CopyAllFields(a, tempA);
                    UpdateAllFields(); addORedit = null;  // ενημέρωση πεδίων με τις τιμές του πίνακα
                    //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                }
            } else {  // αλλιώς αν ο πίνακας είναι άδειος
                ArtistTABLE.clearSelection(); ClearAllFields();  // clear επιλογής πίνακα και πεδίων
                Toolkit.getDefaultToolkit().beep();
                //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
            }
        } else Toolkit.getDefaultToolkit().beep();
    }//GEN-LAST:event_jButtonEDITActionPerformed

    private void jButtonREMOVEActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonREMOVEActionPerformed
        System.out.println(" ~ButtonREMOVE");
        if(ArtistTABLE.getSelectedRow() != -1) {  // αν ο πίνακας δεν είναι άδειος
            int row = ArtistTABLE.getSelectedRow();  // αποθήκευση επιλεγμένης γραμμής
            if(!mm.getExpertMode() && !(rss.okcancelBox(this, "Η επιλεγμένη γραμμή θα διαγραφεί. Είστε σίγουροι;  ") == 0)) return;
                //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
            // αντιστοίχιση αντικειμένου a τύπου Artist στην επιλεγμένη γραμμή του πίνακα, προσωρινή αποθήκευση
            a = listArtist.get(ArtistTABLE.convertRowIndexToModel(row)); CopyAllFields(tempA, a);
            if(rss.accessDB(this, a,"R")) {  // έλεγχος ύπαρξής/επικαιροποίηση as good measure του αντικειμένου a από την ΒΔ
                CopyAllFields(tempA, a);  // εκ νέου προσωρινή αποθήκευση πιθανώς επικαιροποιημένου αντικειμένου από προηγουμένως
                if(rss.accessDB(this, a,"D")) {  // διαγραφή του αντικειμένου a από την ΒΔ
                    // όλα καλά, διαγραφή αντικειμένου a τύπου Artist από τα Συγκροτήματα στα οποία ενδεχομένως συμμετείχε
                    for(MusicGroup musicgroup : a.getMusicGroupCollection()) musicgroup.getArtistCollection().remove(a);
                    // διαγραφή αντικειμένου a τύπου Artist από τα Άλμπουμ που ενδεχομένως δημιούργησε
                    for(Album album : a.getAlbumCollection()) album.setArtistId(null);
                    // διαγραφή αντικειμένου a και από τη λίστα της φόρμας, νέος a Artist και ο παλιός έτοιμος για GarbageCollector!
                    listArtist.remove(a); clearArtist(a); a = null; a = new Artist();
                    // επικαιροποίηση επιλογής γραμμής πίνακα Καλλιτεχνών λόγω διαγραφής
                    if(row >= ArtistTABLE.getRowCount()) row = ArtistTABLE.getRowCount()-1;
                    // έλεγχος επιλογής γραμμής πίνακα Καλλιτεχνών για επικαιροποίηση πεδίων λόγω διαγραφής
                    if(row != -1) {  // αν ο πίνακας που προέκυψε δεν είναι άδειος
                        ArtistTABLE.setRowSelectionInterval(row, row);  // επέλεξε την νέα γραμμή
                        a = listArtist.get(ArtistTABLE.convertRowIndexToModel(row));  // αντιστοίχιση a τύπου Artist στην επιλεγμένη γραμμή 
                        UpdateAllFields();  // ενημέρωση πεδίων με τις τιμές του επιλεγμένου αντικειμένου
                    } else {  // και αν ο πίνακας που προέκυψε είναι άδειος
                        ArtistTABLE.clearSelection(); ClearAllFields();  // clear επιλογής πίνακα και πεδίων
                    }
                    // επιλογή νέου viewport και επικαιροποίηση του πίνακα Καλλιτεχνών
                    ArtistTABLE.scrollRectToVisible(ArtistTABLE.getCellRect(row, 0, true)); //ArtistTABLE.repaint();
                    //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
                } else {  // και αν γενικότερα συμβεί κάποιο σφάλμα κατά τη διαγραφή του αντικειμένου a από την ΒΔ
                    // εκ νέου αντιστοίχιση αντικειμένου a τύπου Artist στην επιλεγμένη γραμμή και επαναφορά
                    a = listArtist.get(ArtistTABLE.convertRowIndexToModel(row)); CopyAllFields(a, tempA);
                    UpdateAllFields();  // ενημέρωση πεδίων με τις τιμές του επιλεγμένου αντικειμένου
                    //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                }
            } else {  // και αν συμβεί σφάλμα κατά τον έλεγχο ύπαρξής/επικαιροποίηση του αντικειμένου a από την ΒΔ
                // εκ νέου αντιστοίχιση αντικειμένου a τύπου Artist στην επιλεγμένη γραμμή και επαναφορά
                a = listArtist.get(ArtistTABLE.convertRowIndexToModel(row)); CopyAllFields(a, tempA);
                UpdateAllFields();  // ενημέρωση πεδίων με τις τιμές του επιλεγμένου αντικειμένου
                //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
            }
        } else {  // και αν ο πίνακας είναι άδειος
            ArtistTABLE.clearSelection(); ClearAllFields();  // clear επιλογής και πεδίων
            Toolkit.getDefaultToolkit().beep();
        }
    }//GEN-LAST:event_jButtonREMOVEActionPerformed

    private void jButtonSAVEActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonSAVEActionPerformed
        System.out.println("  ~ButtonSAVE");
        editStatus = false;  // προστασία των πεδίων από τυχόν αλλαγές
        if(addORedit) {  // αν βρισκόμαστε σε κατάσταση επεξεργασίας ADD
            // επαλήθευση πεδίων και εγγραφή a στη ΒΔ
            if(validateAllFields() && rss.accessDB(this, a,"W")) {  // αν η επιβεβαίωση και η εγγραφή γίνουν σωστά
                listArtist.add(a);  // όλα καλά, προσθήκη του αντικειμένου και στη λίστα
                int row = ArtistTABLE.getRowCount()-1;  // κατέγραψε το νέο μέγεθος του πίνακα
                // αποθήκευσε πιθανή κατάσταση ταξινόμησης και κατάργησε το row sorting του πίνακα
                List<? extends SortKey> myList = ArtistTABLE.getRowSorter().getSortKeys(); ArtistTABLE.getRowSorter().setSortKeys(null);
                ArtistTABLE.setRowSelectionInterval(row, row);  //επέλεξε τη νέα γραμμή (τελευταία σε unsorted model) του πίνακα
                // επαναφορά αποθηκευμένης κατάστασης ταξινόμησης και ρύθμιση viewport πίνακα ώστε η νεοεισηγμένη γραμμή να είναι visible
                ArtistTABLE.getRowSorter().setSortKeys(myList); ArtistTABLE.scrollRectToVisible(ArtistTABLE.getCellRect(row, 0, true));
                // αντιστοίχιση αντικειμένου a τύπου Artist στην επιλεγμένη γραμμή του πίνακα
                a = listArtist.get(ArtistTABLE.convertRowIndexToModel(ArtistTABLE.getSelectedRow()));
                // ενημέρωση πεδίων με τις τιμές του επιλεγμένου αντικειμένου και επικαιροποίηση του πίνακα Καλλιτεχνών
                UpdateAllFields(); //ArtistTABLE.repaint();
                // προετοιμασία ενεργών & ανενεργών components και reset κατάστασης επεξεργασίας
                EnableDisableFields(false); ArtistTABLE.grabFocus(); addORedit = null;
                //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
            } else { clearArtist(a); a = null; a = new Artist(); editStatus = true; }  // κάτι δεν έγινε σωστά, νέος καθαρός Artist a και πίσω στη φόρμα
        } else {  // και αν βρισκόμαστε σε κατάσταση επεξεργασίας EDIT
            // επαλήθευση πεδίων και ανανέωση a στη ΒΔ
            if(validateAllFields() && rss.accessDB(this, a,"U")) { // αν η επιβεβαίωση και η ανανέωση γίνουν σωστά
                // αν όλα καλά, ενημέρωση του αντικειμένου και στη λίστα
                listArtist.set(ArtistTABLE.convertRowIndexToModel(ArtistTABLE.getSelectedRow()), a);
                // και εκ νέου αντιστοίχιση αντικειμένου a τύπου Artist στην επιλεγμένη γραμμή του πίνακα
                a = listArtist.get(ArtistTABLE.convertRowIndexToModel(ArtistTABLE.getSelectedRow()));
                // ενημέρωση πεδίων με τις τιμές του επιλεγμένου αντικειμένου και επικαιροποίηση του πίνακα Καλλιτεχνών
                UpdateAllFields(); //ArtistTABLE.repaint();
                // προετοιμασία ενεργών & ανενεργών components και reset κατάστασης επεξεργασίας
                EnableDisableFields(false); ArtistTABLE.grabFocus(); addORedit = null;
                //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
            } else {  // κάτι δεν έγινε σωστά, ξαναδιάβασμα a από τον πίνακα και πίσω στη φόρμα
                // εκ νέου αντιστοίχιση αντικειμένου a τύπου Artist στην επιλεγμένη γραμμή του πίνακα
                a = listArtist.get(ArtistTABLE.convertRowIndexToModel(ArtistTABLE.getSelectedRow()));
                // και νέος έλεγχος-ανανέωσή των πεδίων του από την ΒΔ
                if(rss.accessDB(this, a,"R")) {  // αν η νέα ανάγνωση γίνει σωστά
                    CopyAllFields(tempA, a); editStatus = true;  // νέα προσωρινή αποθήκευση και πίσω στη φόρμα επεξεργασίας
                    //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                } else {  // το σφάλμα είναι μάλλον σοβαρότερο, πίσω πάλι στον πίνακα    
                    // εκ νέου αντιστοίχιση as good measure αντικειμένου a τύπου Artist στην επιλεγμένη γραμμή του πίνακα και επαναφορά
                    a = listArtist.get(ArtistTABLE.convertRowIndexToModel(ArtistTABLE.getSelectedRow())); CopyAllFields(a, tempA);
                    // ενημέρωση πεδίων με τις τιμές του επιλεγμένου αντικειμένου και επικαιροποίηση του πίνακα Καλλιτεχνών
                    UpdateAllFields(); //ArtistTABLE.repaint();
                    // προετοιμασία ενεργών & ανενεργών components και reset κατάστασης επεξεργασίας
                    EnableDisableFields(false); ArtistTABLE.grabFocus(); addORedit = null;
                    //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                }
            }
        }
    }//GEN-LAST:event_jButtonSAVEActionPerformed

    private void jButtonCANCELActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonCANCELActionPerformed
        System.out.println("  ~ButtonCANCELorESCAPE");
        editStatus = false;  // για προστασία των πεδίων από τυχόν αλλαγές
        // αν έχουν συμβεί αλλαγές και δεν είμαστε σε ExpertMode, επιλογή okcancelBox για επιστροφή στη φόρμα
        if(!compareAllFields() && !mm.getExpertMode() && !(rss.okcancelBox(this, "Οι αλλαγές θα χαθούν. Είστε σίγουροι; ") == 0)) { editStatus = true; return; }
            //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
        // αν ακυρώθηκε ADD το a που δημιουργήθηκε πριν δεν χρειάζεται παρά μόνο στον GarbageCollector, αλλιώς as good measure επαναφορά προσωρινού
        if(addORedit) { clearArtist(a); a = null; a = new Artist(); } else CopyAllFields(a, tempA);
        // τώρα που πλέον η ακύρωση έχει ολοκληρωθεί, πάμε καλόυ κακού για ανανέωση του πίνακα
        if(ArtistTABLE.getSelectedRow() != -1)  {  // αν ο πίνακας δεν είναι άδειος
            // αντιστοίχιση αντικειμένου a τύπου Artist στην επιλεγμένη γραμμή του πίνακα
            a = listArtist.get(ArtistTABLE.convertRowIndexToModel(ArtistTABLE.getSelectedRow()));
            ArtistTABLE.scrollRectToVisible(ArtistTABLE.getCellRect(ArtistTABLE.getSelectedRow(), 0, true));
            UpdateAllFields();  // ενημέρωση πεδίων με τις τιμές του επιλεγμένου αντικειμένου
        } else {  // και αν ο πίνακας είναι άδειος
            ArtistTABLE.clearSelection(); ClearAllFields();  // clear επιλογής πίνακα και πεδίων
        }
        // προετοιμασία ενεργών & ανενεργών components και reset κατάστασης επεξεργασίας
        EnableDisableFields(false); ArtistTABLE.grabFocus(); addORedit = null;
        //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    }//GEN-LAST:event_jButtonCANCELActionPerformed

    private void cboxMusicGenreActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cboxMusicGenreActionPerformed
        if(editStatus) {
            System.out.println("  ~ActionPerformed musicgenre " + fieldFocused);
            cboxMusicGenre.setSelectedIndex(cboxMusicGenre.getSelectedIndex() > 0? cboxMusicGenre.getSelectedIndex() : -1);  // με απορρόφηση του null ?
        }
    }//GEN-LAST:event_cboxMusicGenreActionPerformed

    private void cboxSexActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cboxSexActionPerformed
        if(editStatus) {
            System.out.println("  ~ActionPerformed sex " + fieldFocused);
            cboxSex.setSelectedIndex(cboxSex.getSelectedIndex() > 0? cboxSex.getSelectedIndex() : -1);  // με  απορρόφηση του null ?
            if(cboxSex.getSelectedIndex() == 1) {  // θήλυ
                jLabelARTISTmale.setVisible(false); jLabelARTISTfemale.setVisible(true);
            } else if(cboxSex.getSelectedIndex() == 2) {  // άρρεν
                jLabelARTISTmale.setVisible(true); jLabelARTISTfemale.setVisible(false);
            } else { jLabelARTISTmale.setVisible(false); jLabelARTISTfemale.setVisible(false); }  // ούτε άρρεν ούτε θήλυ
        }
    }//GEN-LAST:event_cboxSexActionPerformed

    private void jPanelFORMFocusGained(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_jPanelFORMFocusGained
        System.out.println(" ~FocusGained jpanelform " + fieldFocused);
        fieldFocused = 0;
    }//GEN-LAST:event_jPanelFORMFocusGained

    private void cboxMusicGenreKeyPressed(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_cboxMusicGenreKeyPressed
        // αν πατηθεί Escape και δεν είναι ανοικτό το popup menu, έξοδος από τη φόρμα επεξεργασίας
        if(evt.getKeyCode() == 27 && !cboxMusicGenre.isPopupVisible()) jButtonCANCELActionPerformed(null);
    }//GEN-LAST:event_cboxMusicGenreKeyPressed

    private void cboxSexKeyPressed(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_cboxSexKeyPressed
        // αν πατηθεί Escape και δεν είναι ανοικτό το popup menu, έξοδος από τη φόρμα επεξεργασίας
        if(evt.getKeyCode() == 27 && !cboxSex.isPopupVisible()) jButtonCANCELActionPerformed(null);
    }//GEN-LAST:event_cboxSexKeyPressed

    private void ArtistTABLEMousePressed(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_ArtistTABLEMousePressed
        if(!editStatus) {
            ArtistTABLEKeyReleased(null);  // ίδιος κώδικας με ArtistTABLEKeyReleased
        }
    }//GEN-LAST:event_ArtistTABLEMousePressed

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JTable ArtistTABLE;
    private javax.swing.JComboBox cboxMusicGenre;
    private javax.swing.JComboBox cboxSex;
    private javax.swing.JButton jButtonADD;
    private javax.swing.JButton jButtonCANCEL;
    private javax.swing.JButton jButtonEDIT;
    private javax.swing.JButton jButtonREMOVE;
    private javax.swing.JButton jButtonRETURN;
    private javax.swing.JButton jButtonSAVE;
    private javax.swing.JLabel jLabelARTISTfemale;
    private javax.swing.JLabel jLabelARTISTmale;
    private javax.swing.JLabel jLabelBACKGROUND;
    private javax.swing.JLabel jLabelFORM;
    private javax.swing.JLabel jLabelMAINTABLE;
    private javax.swing.JLabel jLabelRSSLOGO;
    private javax.swing.JLabel jLabelRSTEAM;
    private javax.swing.JPanel jPanelFORM;
    private javax.swing.JScrollPane jScrollPaneMAINTABLE;
    private javax.swing.JSeparator jSeparatorLINE;
    private java.util.List<Artist> listArtist;
    private java.util.List<MusicGenre> listMusicGenre;
    private javax.persistence.Query queryArtist;
    private javax.persistence.Query queryMusicGenre;
    private javax.swing.JTextField txtArtisticName;
    private javax.swing.JTextField txtBirthDate;
    private javax.swing.JTextField txtBirthPlace;
    private javax.swing.JTextField txtFirstName;
    private javax.swing.JTextField txtLastName;
    private org.jdesktop.beansbinding.BindingGroup bindingGroup;
    // End of variables declaration//GEN-END:variables
}